package jane.test.async;

import java.util.Comparator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.PriorityBlockingQueue;

public final class AsyncManager
{
	private static final class TaskWrap extends AsyncTimerTask
	{
		private final Runnable _r;

		public TaskWrap(int delayMs, Runnable r)
		{
			super(delayMs);
			_r = r;
		}

		@Override
		public void run()
		{
			_r.run();
		}
	}

	private static final class TaskComparator implements Comparator<AsyncTimerTask>
	{
		@Override
		public int compare(AsyncTimerTask task1, AsyncTimerTask task2)
		{
			long d = task1._time - task2._time;
			return d < 0 ? -1 : (d > 0 ? 1 : 0);
		}
	}

	private static final AsyncManager					_instance	= new AsyncManager();
	private static AsyncException						_ae;
	private final ConcurrentLinkedQueue<Runnable>		_readyQueue	= new ConcurrentLinkedQueue<>();
	private final PriorityBlockingQueue<AsyncTimerTask>	_taskQueue	= new PriorityBlockingQueue<>(16, new TaskComparator());

	public static AsyncManager get()
	{
		return _instance;
	}

	public static AsyncException getAsyncException()
	{
		return _ae;
	}

	public static void setAsyncException(AsyncException ae)
	{
		_ae = ae;
	}

	static void onException(Runnable r, Throwable e)
	{
		AsyncException ae = _ae;
		if (ae != null)
			ae.onException(r, e);
	}

	public void submit(Runnable r)
	{
		_readyQueue.offer(r);
	}

	public void submit(AsyncTimerTask task)
	{
		if (task != null)
			_taskQueue.offer(task);
	}

	public void submit(int delayMs, Runnable r)
	{
		if (r != null)
			_taskQueue.offer(new TaskWrap(delayMs, r));
	}

	public int tick()
	{
		int done = 0;
		for (long time = System.currentTimeMillis();;)
		{
			AsyncTimerTask task = _taskQueue.peek();
			if (task == null || task._time > time)
				break;
			try
			{
				task = _taskQueue.poll();
				if (task == null)
					break;
				task.run();
				++done;
			}
			catch (Throwable e)
			{
				AsyncManager.onException(task, e);
			}
		}

		for (int n = _readyQueue.size(); n > 0; --n)
		{
			Runnable r = _readyQueue.poll();
			if (r == null)
				break;
			try
			{
				r.run();
				++done;
			}
			catch (Throwable e)
			{
				onException(r, e);
			}
		}
		return done;
	}
}
